package com.sticktogether;


import java.util.Calendar;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;

import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.CancelableCallback;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.sticktogether.multiphotoselect.MultiPhotoSelectActivity;
import com.sticktogether.navigation.Navigation;
import com.sticktogether.server.ServerCommunication;
import com.sticktogether.usermanagement.Member;

public class MapViewActivity extends FragmentActivity {


	private OnClickListener		mClickListener;
	private OnLongClickListener	mLongClickListener;
	private Button				b_nav;
	private Button				b_refresh;
	private Button				b_mapType;
	private Button				b_markerList;
	private Button				b_exit;
	private Button				b_settings;
	private Button				b_kamera;
	private Button				b_portal;
	private ProgressBar			progressbar;
    public GoogleMap 			mMapView;
	private String 				mOrigin; 
	private int[]				buttonClickAction = new int[3];
	
	public int					showInfoWindowOfThisMember;
	public String				showInfoWindowOfThisMarker;
	private Intent 				nav_Intent = new Intent();
	
	public static boolean 		navigating = false;
	private Drawable 			mMarkerDrawable;
	public static int			mMarkerWidth;
	public static int			mMarkerHeight;
	
	private SharedPreferences 	mSharedPref;
	private PowerManager.WakeLock wakelock;
	
	@SuppressWarnings("deprecation")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
    	// remove title
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // set FullScreen
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		
		setContentView(R.layout.activity_mapview);
		
		// Maybe this solves an issue, that sometimes the markers won't be loaded, even not when clicking the refresh button.
		// The reason for that is that onPostExecute is not called in AsyncTask due to the RuntimeException: Handler sending message to a Handler on a dead thread
		// Compare: http://stackoverflow.com/questions/4280330/onpostexecute-not-being-called-in-asynctask-handler-runtime-exception
		try {
			Class.forName("android.os.AsyncTask");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		ServerCommunication.map = this;
		
		mSharedPref = PreferenceManager.getDefaultSharedPreferences(this);
		
    	nav_Intent.setClass(MapViewActivity.this, NavigationActivity.class);
		
		initMapView();
		initButtons();
		
		//Used, when marker was clicked in marker list
	    showInfoWindowOfThisMember = getIntent().getIntExtra("showThisMemberOnMap",0);
	    showInfoWindowOfThisMarker = getIntent().getStringExtra("showThisPinOnMap");
	    
		wakelock = ((PowerManager) getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "map");
	}
	
	@Override
	protected void onResume() {
	    super.onResume();
    	
	    //check if tracking is enabled
	    //if not -> don't show map
    	if(	!mSharedPref.getBoolean("tracking_enable", true) ||
    		(!((LocationManager) getSystemService( LOCATION_SERVICE )).isProviderEnabled( LocationManager.GPS_PROVIDER ) &&
    		!((LocationManager) getSystemService( LOCATION_SERVICE )).isProviderEnabled( LocationManager.NETWORK_PROVIDER ))) {
    		Intent intent = new Intent(this,LogoutActivity.class);
        	this.startActivity(intent);
    	}
    	else {
    		//listeners and updates are activated
    		toggleViewAvailability(b_nav, progressbar, true);
		    navigating = false;
		    ServerCommunication.getMemberListFromServer();
		    ServerCommunication.getPinListFromServer();
		    //start receiving changes of positions from now on
		    ServerCommunication.ReceivePositionUpdates(this, true);
		    //service will track the user per gps as long as map is in foreground
			if(ServerCommunication.currentService!=null)
				ServerCommunication.currentService.updateLocationListener(true);
			// don't switch off the display automatically as long as the user is in map view
		    wakelock.acquire();
    	}
	}
	
	@Override
	protected void onPause() {
		super.onPause();
	    // ok, allow the display to switch off automatically again as soon as the mapviewactivity is paused.
	    if(wakelock.isHeld()) wakelock.release();
	    //service won't track you per gps anymore, except gps mode is enabled
	    if(ServerCommunication.currentService!=null)
	    	ServerCommunication.currentService.updateLocationListener(false);
		//stop receiving changes of positions from now on
	    ServerCommunication.ReceivePositionUpdates(this, false);
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    // Handle item selection
    	Intent i = new Intent();
	    switch (item.getItemId()) {
	    	// Settings button was pressed
	        case R.id.menu_settings:
	    		i.setClass(this, SettingsActivity.class);
	    		startActivity(i);
	            return true;
	        case R.id.menu_upload:
	    		i.setClass(this, MultiPhotoSelectActivity.class);
	    		startActivity(i);
	            return true;
	        case R.id.menu_portal:
	        	Intent portalIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.wirhassenalledenjul.de/portal.php"));
	        	startActivity(portalIntent);
	            return true;
	        default:
	            return super.onOptionsItemSelected(item);
	    }
	}
	
	@TargetApi(Build.VERSION_CODES.KITKAT)
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
    	super.onWindowFocusChanged(hasFocus);
    	if(Build.VERSION.SDK_INT >= 19){
	        if (hasFocus) {
				View mDecorView = getWindow().getDecorView();
				mDecorView.setSystemUiVisibility(
					View.SYSTEM_UI_FLAG_LAYOUT_STABLE
					| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
					| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
					| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
					| View.SYSTEM_UI_FLAG_FULLSCREEN
					| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
			}
    	}
    }
    
    
	/**
	 * Initialize Map View and own position 
	 */
	private void initMapView(){
		final LatLng MUNICH = new LatLng(48.2,11.6);
		
		//marker and its properties
		mMarkerDrawable = getResources().getDrawable(R.drawable.marker);
		mMarkerWidth = mMarkerDrawable.getIntrinsicWidth();
		mMarkerHeight = mMarkerDrawable.getIntrinsicHeight();
		
		progressbar = (ProgressBar)findViewById(R.id.progressBar);
		//Check that Google Play Services is available on the device at runtime to protect against error cases
		GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
				
		// map properties
		mMapView = ((SupportMapFragment)  getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
		mMapView.moveCamera(CameraUpdateFactory.newLatLngZoom(MUNICH, 11));
		//mMapView.setMyLocationEnabled(true);
		mMapView.setIndoorEnabled(true);
		
		//long click -> add marker
		mMapView.setOnMapLongClickListener(new OnMapLongClickListener() {
			
			public void onMapLongClick(final LatLng pos) {
				final EditText input = new EditText(MapViewActivity.this);
				input.setLines(1);
				new AlertDialog.Builder(MapViewActivity.this)
			    .setTitle(MapViewActivity.this.getString(R.string.add_pin_title))
			    .setView(input)
			    .setPositiveButton(MapViewActivity.this.getString(R.string.ok), new DialogInterface.OnClickListener() {
			        public void onClick(DialogInterface dialog, int whichButton) {
			            Editable value = input.getText();
			            if(!value.toString().trim().equals("")) {
			            	ServerCommunication.putMarker(pos.latitude, pos.longitude, value.toString().trim());
			            }
			            else {
			            	postToast(getString(R.string.add_pin_no_title_added));
			            }
			            
			        }
			    })
			    .setNegativeButton(MapViewActivity.this.getString(R.string.cancel), new DialogInterface.OnClickListener() {
			        public void onClick(DialogInterface dialog, int whichButton) {}
			    }).show();			
			}
		});
		
		//click on marker's info window -> show options
		mMapView.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
			
			public void onInfoWindowClick(final Marker m) {
				CharSequence[] options;
				if(m.isDraggable()) {
					if(b_nav.isClickable()) {
						CharSequence[] opts = {getString(R.string.public_transport_here), getString(R.string.delete), getString(R.string.cancel)};
						options = opts;
						buttonClickAction[0] = 1;
						buttonClickAction[1] = 2;
						buttonClickAction[2] = -1;
					}
					else {
						CharSequence[] opts = {getString(R.string.delete), getString(R.string.cancel)};
						options = opts;
						buttonClickAction[0] = 2;
						buttonClickAction[1] = -1;
					}
				}
				else {
					if(b_nav.isClickable()) {
						CharSequence[] opts = {getString(R.string.public_transport_here), getString(R.string.cancel)};
						
						options = opts;
						buttonClickAction[0] = 1;
						buttonClickAction[1] = -1;
					}
					else {
						CharSequence[] opts = {getString(R.string.cancel)};
						options = opts;
						buttonClickAction[0] = -1;
					}
				}
				new AlertDialog.Builder(MapViewActivity.this)
			    .setTitle(MapViewActivity.this.getString(R.string.marker_options) + ": " + m.getTitle())
			    .setSingleChoiceItems(options, -1, new android.content.DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						if(buttonClickAction[which]==1) {
							// the user has selected to navigate here, so ask him when.
							showTimePickerDialog(m.getPosition().latitude + "," + m.getPosition().longitude);
						}
						else if(buttonClickAction[which]==2) {
							// The user has selected to delete the selected marker
							ServerCommunication.deleteMarker(m.getTitle());
						}
						dialog.dismiss();
					};
				})
			    .show();	
			}
		});
		
		//move marker
		mMapView.setOnMarkerDragListener(new OnMarkerDragListener() {
			
			public void onMarkerDragStart(Marker arg0) {}
			
			public void onMarkerDragEnd(Marker m) {
				ServerCommunication.moveMarker(m.getPosition().latitude, m.getPosition().longitude, m.getTitle());
			}
			
			public void onMarkerDrag(Marker arg0) {}
		});
		
	}
	
	/**
	 * We have a destination that we want to navigate to.
	 * So ask the user when he wants to to.
	 * If the selected hour is before the hour of the current time, he already wants to travel on the next day.
	 * In this case we just add 1 to the current day. As all of this will be converted to a timestamp, 
	 * it is no problem at all if we have 32 days, for example.
	 * 
	 * @param destination
	 */
	public void showTimePickerDialog(final String destination) {
		final Calendar c = Calendar.getInstance();
		TimePickerDialog.OnTimeSetListener timePickerListener = new TimePickerDialog.OnTimeSetListener() {
			public void onTimeSet(TimePicker view, int selectedHour, int selectedMinute) {
				if (selectedHour >= c.get(Calendar.HOUR_OF_DAY)) {
					navigateHere(destination, c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH),  selectedHour, selectedMinute);
				}
				else {
					navigateHere(destination, c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH)+1,  selectedHour, selectedMinute);
				}
			}
		};
		new TimePickerDialog(MapViewActivity.this, timePickerListener, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show();
	}
	
	/**
	 * Initialize button views
	 */
	private void initButtons(){
		
		
		//Listener for all buttons
		mClickListener = new OnClickListener(){
			public void onClick(View view) {
				
				Intent i = new Intent();
				
				switch(view.getId()){
				
				case R.id.b_navigation:
					postToast(getString(R.string.new_navigation));
					if(NavigationActivity.nav_Info != null){
		            	startActivity(nav_Intent);
					}
					break;
					
				case R.id.b_refresh:
					postToast(getString(R.string.refresh_markers));
					ServerCommunication.getMemberListFromServer();
					ServerCommunication.getPinListFromServer();
					break;
					
				case R.id.b_maptype:
					//Indoor does not always work, when activating it only once in the code
					//To be sure, it's activated here again
					if(mMapView.getMapType() == GoogleMap.MAP_TYPE_NORMAL){
						mMapView.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
						mMapView.setIndoorEnabled(true);
					}
					else{
						mMapView.setMapType(GoogleMap.MAP_TYPE_NORMAL);
						mMapView.setIndoorEnabled(true);
					}
					break;
					
				case R.id.b_markerlist:
					i.setClass(MapViewActivity.this, SessionActivity.class);
					startActivity(i);
					break;
					
				case R.id.b_exit:
				    Intent intent = new Intent(Intent.ACTION_MAIN);
				    intent.addCategory(Intent.CATEGORY_HOME);
				    startActivity(intent);
				    break;
				case R.id.b_settings:
		    		i.setClass(MapViewActivity.this, SettingsActivity.class);
		    		startActivity(i);
					break;
				case R.id.b_kamera:
		    		i.setClass(MapViewActivity.this, MultiPhotoSelectActivity.class);
		    		startActivity(i);
		    		break;
		        case R.id.b_portal:
		        	Intent portalIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.wirhassenalledenjul.de/portal.php"));
		        	startActivity(portalIntent);
					break;
				}
			}
		};
		
		//long clicking on navigation button -> new navigation
		mLongClickListener = new OnLongClickListener(){
			public boolean onLongClick(View view) {
				switch(view.getId()){
				case R.id.b_navigation:
		    		initNavigationDialog();
					break;
				}
				return false;
			}
		};
		
		//definition of all buttons and their images and listeners
    	b_nav = (Button) findViewById(R.id.b_navigation);
		b_nav.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.rail), null, null, null);
    	b_nav.setOnClickListener(mClickListener);
    	b_nav.setOnLongClickListener(mLongClickListener);
    	
	    b_exit = (Button) findViewById(R.id.b_exit);
	    b_exit.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.exit), null, null, null);
	    b_exit.setOnClickListener(mClickListener);
	    
	    b_settings = (Button) findViewById(R.id.b_settings);
	    b_settings.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.settings), null, null, null);
	    b_settings.setOnClickListener(mClickListener);
	    
	    b_portal = (Button) findViewById(R.id.b_portal);
	    b_portal.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.portal), null, null, null);
	    b_portal.setOnClickListener(mClickListener);
	    
	    b_kamera = (Button) findViewById(R.id.b_kamera);
	    b_kamera.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.kamera), null, null, null);
	    b_kamera.setOnClickListener(mClickListener);
	    
	    b_refresh = (Button) findViewById(R.id.b_refresh);
	    b_refresh.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.refresh), null, null, null);
	    b_refresh.setOnClickListener(mClickListener);
	    
	    b_mapType = (Button) findViewById(R.id.b_maptype);
	    b_mapType.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.layers), null, null, null);
	    b_mapType.setOnClickListener(mClickListener);
	    
	    b_markerList = (Button) findViewById(R.id.b_markerlist);
	    b_markerList.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.contact), null, null, null);
	    b_markerList.setOnClickListener(mClickListener);
	    
	}
	
	
	
	/**
	 * Initialize the Dialog for typing destination in case there are no predefined locations
	 */
	private void initNavigationDialog(){
		final Dialog dialog = new Dialog(this);
		dialog.setContentView(R.layout.dialog_edittext);
		dialog.setTitle(getString(R.string.dialog_title_location));
		final TextView arrival = (TextView)dialog.findViewById(R.id.edit_Text);
		final Button dialogOK = (Button) dialog.findViewById(R.id.dialogButtonOK);
		final Button dialogCancel = (Button) dialog.findViewById(R.id.dialogButtonCancel);
		dialogCancel.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				dialog.dismiss();
			}
		});

		arrival.setText("");
		arrival.setHint(getString(R.string.destination));
		
		dialogOK.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				showTimePickerDialog(arrival.getText().toString());
				dialog.dismiss();
			}
		});

		dialog.show();
	}
	
	/**
	 * Navigate to specific location at specific time
	 * 
	 * @param destination
	 * @param year
	 * @param month
	 * @param day
	 * @param hours
	 * @param minutes
	 */
	private void navigateHere(final String destination, final int year, final int month, final int day, final int hours, final int minutes) {
		
		if(mMapView.getMyLocation()!=null){
			//Location of user unknown -> let him type in his location or address
			mOrigin = mMapView.getMyLocation().getLatitude()+","+mMapView.getMyLocation().getLongitude();
			
		}
		else if(ServerCommunication.currentService.mCurrentLocation!=null) {
			//If service has a position
			mOrigin = ServerCommunication.currentService.mCurrentLocation.getLatitude()+","+ServerCommunication.currentService.mCurrentLocation.getLongitude();
		}
		else{
			//own location not determined, yet
			postToast(getString(R.string.wait_for_gps));
			return;
		}
	
		//disable the navigation option as long as the thread is already searching
		toggleViewAvailability(b_nav, progressbar, false);
		navigating = true;

		//Start a thread for working on the navigation request
	    new Thread(new Runnable() {
	        public void run() {
	    		Navigation nav = new Navigation(mOrigin, destination, year, month, day, hours, minutes, "transit", Locale.getDefault().toString(), true, false);

	    		if(nav.getError() != null)
	            
	            //Bring all necessary information for navigation steps
	    		NavigationActivity.nav_icons = nav.getAllIcons();
	            NavigationActivity.nav_Info = nav.getAllSteps();
	            
	            startActivity(nav_Intent);
	        }
	    }).start();
	}
	
	/**
	 * Disable a wished view, make it invisible and show a specific progressbar instead
	 * or enable the view again after some process finished
	 * @param view
	 * @param progress
	 * @param setAvailable
	 */
	private void toggleViewAvailability(View view, ProgressBar progress, boolean setAvailable){
		if(setAvailable){
			view.setVisibility(View.VISIBLE);
	    	view.setClickable(true);
	    	progress.setVisibility(View.INVISIBLE);
		}
		else{
			view.setVisibility(View.INVISIBLE);
	    	view.setClickable(false);
	    	progress.setVisibility(View.VISIBLE);
		}
	}
	
	
	
	/**
	 * Update the locations of the members
	 */
	public void updateMembers(){

		Iterator<Map.Entry<Integer, Member>> it = ServerCommunication.members.entrySet().iterator();
		while (it.hasNext()) {
			updateMember(it.next().getValue());
		}
	}
	
	/**
	 * Update a specific member
	 * 
	 * @param m
	 */
	public void updateMember(final Member m) {
		if(m.mMarker==null) {
			showMember(m);
		}
		else {
			m.mMarker.setPosition(new LatLng(m.getLatitude(),m.getLongitude()));
		}
		// check if we have been in the memberlist and selected a user we want to see on the map
		// if so showInfoWindow == 1 and the id of the user we want to see is saved in showInfoWindowOfThisMember
		// so check if this is our id and if so... zoom to the member :)
		if(ServerCommunication.showInfoWindow == 1 && showInfoWindowOfThisMember == m.getId()) {
			ServerCommunication.showInfoWindow = 0;
			mMapView.animateCamera(CameraUpdateFactory.newLatLngZoom(m.getLocation(), Integer.parseInt(mSharedPref.getString("zoomlevel", "15"))), Integer.parseInt(mSharedPref.getString("zoomspeed", "3000")), new CancelableCallback() {

				public void onCancel() {
				}

				public void onFinish() {
					m.mMarker.showInfoWindow();
				}
			});
		}
	}
	
	
	/**
	 * Show members on the map
	 * @param m Member to show
	 */
	private void showMember(Member m){
		MarkerOptions mMarkerOptions = new MarkerOptions()
			.position(m.getLocation())
			.title(m.getUsername())
			.snippet(m.getLastUpdateTime())
			.icon(m.getBitmapDescriptor())
			.anchor(0.5f, 0.5f);
		m.mMarker = mMapView.addMarker(mMarkerOptions);
	}
	
	/**
	 * Place pin on map
	 * check if we have been in the memberlist and selected a marker we want to see on the map
	 * if so showInfoWindow == 2 and the title of the marker we want to see is saved in showInfoWindowOfThisMarker
	 * so check if this is our title and if so... zoom to the marker 
	 * 
	 * @param mo Marker options
	 * @return	marker with specified options
	 */
	public Marker showPin(MarkerOptions mo){
		final Marker marker = mMapView.addMarker(mo);
		if(ServerCommunication.showInfoWindow == 2 && showInfoWindowOfThisMarker != null && showInfoWindowOfThisMarker.equals(mo.getTitle())) {
			ServerCommunication.showInfoWindow = 0;
			mMapView.animateCamera(CameraUpdateFactory.newLatLngZoom(mo.getPosition(), Integer.parseInt(mSharedPref.getString("zoomlevel", "15"))), Integer.parseInt(mSharedPref.getString("zoomspeed", "3000")), new CancelableCallback() {

				public void onCancel() {
				}

				public void onFinish() {
					marker.showInfoWindow();
				}
			});
		}
		return marker;
	}
	
	
    /**
     * Method for posting a toast on display -> reducing code
     * @param toast	String containing toast message
     */
    private void postToast(String toast){
    	Toast.makeText(getBaseContext(), toast, Toast.LENGTH_LONG).show();
    }

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.menu, menu);
		return true;
	}
	
	@Override
	public void onBackPressed() {
		Intent i = new Intent();
		i.setAction(Intent.ACTION_MAIN);
		i.addCategory(Intent.CATEGORY_HOME);
		this.startActivity(i); 
	}


}
